﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////


#pragma once

#include "GcmpUiDocumentImpl.h"

// from gnuf
#include "GnufStateGuard.h"
#include "GnufImpl.h"
#include "GnufQtObjectPtr.h"
#include "GnufTrash.h"

// from gcmp
#include "IUiView.h"

#ifdef _GNUF_WIP_REFACTOR_UIDOC_UIVIEW_LOGIC_

class QWidget;
class QObject;
namespace gcmp {
    class IUiDocument;
    class IModelView;
    class UiViewEvent;
    class ElementId;
    class UIDocument;
    class UIDocumentManager;
    class UIViewQtFunicle;
    
    namespace impl {
        class GNUF_IMPL_CLASS(UIView);
        class GNUF_IMPL_CLASS(UIDocument);
        class QtUIView;
    }
    
    class GCMP_UIDOCUMENTIMPL_EXPORT IUIViewInspector : public gcmp::WeakReferenceable
    {
    public:
        virtual void onElapsed(int elapsed, const void * pQtEvt /* QEvent Object */) = 0;
    };
    struct GCMP_UIDOCUMENTIMPL_EXPORT UIViewInspection final
    {
        using EventType = std::uint16_t;
        using Events    = std::unordered_map< UIViewInspection::EventType, int /* threshold */ >;

        enum class ELogOption : std::uint8_t {
            eFYI    = 1 << 0, ///< log in the journal, by FYI
        };
        
        ///\return Return ture if inspect the TEventType.
        ///\param et is the TEventType.
        ///\param pThreshold is the threshold.
        template <typename TEventType>
        bool doesInspect(TEventType et, int * pThreshold = nullptr) const
        {
            const auto  type = static_cast<UIViewInspection::Events::value_type::first_type>(et);
            const auto found = this->events.find(type);
            if (found != this->events.end()) {
                if (nullptr != pThreshold)
                    *pThreshold = (*found).second; // return the threshold if needed

                return true;
            }
            return false;
        }
        ///\brief Inspect the TEventType.
        ///\param et is the TEventType.
        ///\param threshold is the threshold. Trigger logging when the elapsed time exceeds it.
        template <typename TEventType>
        void inspect(TEventType et, int threshold = 20 /* milliseconds */)
        {
            const auto type = static_cast<UIViewInspection::Events::value_type::first_type>(et);
            this->events.insert(std::make_pair(type, threshold));
        }

        ///\brief Set the log option.
        void set(UIViewInspection::ELogOption opt)
        { this->logOptions |= static_cast< std::uint8_t >(opt); }
        ///\return Return ture if the log option exists.
        bool test(UIViewInspection::ELogOption opt) const
        { return 0 != (this->logOptions & static_cast< std::uint8_t >(opt)); }
        
        UIViewInspection::Events events;
        std::uint8_t logOptions = 0;
        gcmp::WeakPtr< IUIViewInspector > wpInspector;
    };

    class GCMP_UIDOCUMENTIMPL_EXPORT UIView : public gcmp::IUiView, public gcmp::Trash
    {
        DEFINE_CAST_DERIVED(gcmp::UIView, gcmp::IUiView);
        GNUF_IMPL_USE(UIView);

    public:
        enum class EType : std::uint8_t {
            eUnknown,
            eModel,
            eDrawing,
            eViewport
        };

        enum class EState : std::uint8_t {
            eUnknown    = 0,
            eCreated    = 1,
            eOpened     = 2,
            eActive     = 3,
            eClosed     = 4
        };

    protected:
        explicit UIView(UIDocument * pRoot, UIView * pParent, UIView::EType);

    public:
        virtual ~UIView();

        ///\return Return the type. If failed, return UIView::EType::eUnknown.
        UIView::EType type() const;

        ///\return Return the state. If failed, return UIView::EState::eUnknown.
        UIView::EState state() const;

        ///\return Cast to the UIView of the QObject.
        template <typename TUIView>
        static TUIView * castTo(QObject * qtObj)
        {
            static_assert(std::is_base_of<gcmp::IUiView, TUIView>::value,
                "This class must inherit from gcmp::IUiView class!");
            if (gcmp::IUiView * pUiView = UIView::oneOf(qtObj))
                return gcmp::quick_cast<TUIView>(pUiView);

            return nullptr;
        }

        ///\return Cast to the QWidget of the gcmp::IUiView.
        template <typename TUIView>
        static QWidget * castToQWidget(TUIView * pUIView)
        {
            static_assert(std::is_base_of<gcmp::IUiView, TUIView>::value,
                "This class must inherit from gcmp::IUiView class!");
            if (nullptr != pUIView)
                if (UIView * pTarget = gcmp::quick_cast<UIView>(pUIView))
                    return UIView::qtWgtOf(pTarget);

            return nullptr;
        }

        ///\return Return the parent of the gcmp::IUiView.
        static UIView * parentOf(gcmp::IUiView *);
        static const UIView * parentOf(const gcmp::IUiView *);

        ///\return Return the QWidget of the active UIView.
        static QWidget * qtWidgetOfActiveUIView();
        
        ///\return Return the UIDocumentManager.
        const UIDocumentManager * manager() const;
        UIDocumentManager * manager();

        ///\return Return the UIDocument.
        const UIDocument * uiDocument() const;
        UIDocument * uiDocument();

        ///\return Return the parent.
        const UIView * parent() const;
        UIView * parent();
        
        ///\brief Set the inspection.
        void setInspection(gcmp::OwnerPtr< UIViewInspection > &&);
        const UIViewInspection * inspection() const;

    protected:
        const gcmp::impl::QtUIView * qtUIView() const;
        gcmp::impl::QtUIView * qtUIView();

        friend class gcmp::impl::QtUIView;
        virtual const UIViewQtFunicle * toFunicle() const = 0;
        virtual UIViewQtFunicle * toFunicle() = 0;

        // Inherits from gcmp::Trash...
        virtual void onDiscarded() override final;
        virtual bool onAllowToDelete() const override;
        
        // Give the derived an opportunity to make a response.
        friend class gcmp::impl::UIViewImpl;
        virtual void onPostClosed() = 0;
        virtual void onPreActivated(bool & approved,
            bool & needEventPreDone, bool & emitEventPostDoneLater) {}
        virtual void onPostActivated() {}
        virtual void onPreDeactivated(
            const UIView * pWillActivate, bool & approved, bool & overtly) {}
        virtual gcmp::OwnerPtr< gcmp::UIView > makeChild(
            gcmp::IModelView *, const std::wstring & uniqueTag) { return nullptr; }
        virtual bool isSeenAsActive(const bool programmatically) const { return false; }

    private:
        static UIView  *   oneOf(QObject *);
        static QWidget * qtWgtOf(UIView  *);

    public:
        // Inherits from gcmp::IUiView...
        virtual gcmp::IUiView * Parent();
        virtual const gcmp::IUiView * Parent() const;
        virtual int GetId() const override final;
    };

}

namespace gcmp { namespace impl {
    class UIViewFamily;

    class GNUF_IMPL_CLASS(UIView) final
    {
        GNUF_IMPL_USED_BY(UIView);
    public:
        GNUF_IMPL_CLASS(UIView)(UIView * pItf, UIDocument * pRoot, UIView::EType);
        GNUF_IMPL_CLASS(UIView)(UIView * pItf, UIView *   pParent, UIView::EType);
        ~GNUF_IMPL_CLASS(UIView)();

        int id() const { return m_runtimeId; }

        void onDiscarded();

        UIView::EType type() const { return m_type; }

        UIView::EState state() const { return m_state(); }
        bool isSwitchingState(UIView::EState * pNewState = nullptr) const;
        bool isReady()  const { return (UIView::EState::eOpened == m_state()); }
        bool isActive() const { return (UIView::EState::eActive == m_state()); }
        bool isClosing()const { return m_closing; }
        
        bool initialize(const gcmp::ElementId & modelViewId, const bool disableVBO);
        // An UIView is owned by the parent UIView or the UIDocument. When the owner is
        // closed, all the owned ones have to get closed. This means passively closing.
        bool close(const bool passively = false);
        bool activate(const bool programmatically, const bool overtly = true);
        bool deactivateDueTo(const UIView * pWillActivate);
        
        // Return the UIDocumentManager.
        const UIDocumentManager * manager() const;
        UIDocumentManager * manager();
        
        // Return the UIDocument.
        const UIDocument * uiDocument() const;
        UIDocument * uiDocument();
        
        // Return the parent.
        const UIView * parent() const;
        UIView * parent();

        // Return the family.
        const UIViewFamily * family() const;
        UIViewFamily * family();

        // Return the QtUIView.
        const gcmp::impl::QtUIView * qtUIView() const;
        gcmp::impl::QtUIView * qtUIView();

        // Add a sub-UIView.
        UIView * addChild(gcmp::IModelView *, const std::wstring & uniqueTag);
        
        void travelRecursively(const std::function<bool(const UIView *)> & callback) const;
        void travelRecursively(const std::function<bool(UIView *)> & callback);
        
        void setQtParent(QWidget *);

        bool isVisible() const;
        bool setVisible(bool);

        bool isSeenAsActive(const bool programmatically) const;
        
        void setInspection(gcmp::OwnerPtr< UIViewInspection > &&);
        const UIViewInspection * inspection() const { return m_opInspection.get(); }
              UIViewInspection * inspection()       { return m_opInspection.get(); }

    private:
        void initializeRuntimeId();

        // Remove the sub-UIView.
        friend class gcmp::impl::UIDocumentImpl;
        bool removeSubUIView(UIView *);

        void clearCanvas(const bool passively);
        
        bool onPreClosed(const bool passively);
        bool onPreActivated(bool & needEventPreDone, bool & emitEventPostDoneLater);
        void onPostActivated(const bool overtly);
        bool onPreDeactivated(const UIView * pWillActivate, bool & overtly);
        
        gcmp::UiViewEvent * evt();

        void doInitialize(const gcmp::ElementId & modelViewId, const bool disableVBO);
        void doActivate();
        void doDeactivate();
        
        int m_runtimeId = -1; // invalid, by default
        const UIView::EType m_type = UIView::EType::eUnknown;
        gcmp::StateGuard< UIView::EState > m_state;
        gcmp::QtAutoPtr< gcmp::impl::QtUIView > m_apQtUIView;
        gcmp::OwnerPtr< UIViewFamily > m_opFamily;
        gcmp::OwnerPtr< UIViewInspection > m_opInspection;

        struct Closing {
            bool & m_rval;
            Closing(bool &);
            ~Closing();
        };
        bool m_closing = false;
    };
} }

#endif // !_GNUF_WIP_REFACTOR_UIDOC_UIVIEW_LOGIC_